home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / fax / src / util / xferstats.sh < prev   
Linux/UNIX/POSIX Shell Script  |  1994-08-01  |  10KB  |  409 lines

  1. #! /bin/sh
  2. #    $Header: /usr/people/sam/fax/util/RCS/xferstats.sh,v 1.8 1994/04/18 21:35:05 sam Rel $
  3. #
  4. # FlexFAX Facsimile Software
  5. #
  6. # Copyright (c) 1990, 1991, 1992, 1993, 1994 Sam Leffler
  7. # Copyright (c) 1991, 1992, 1993, 1994 Silicon Graphics, Inc.
  8. # Permission to use, copy, modify, distribute, and sell this software and 
  9. # its documentation for any purpose is hereby granted without fee, provided
  10. # that (i) the above copyright notices and this permission notice appear in
  11. # all copies of the software and related documentation, and (ii) the names of
  12. # Sam Leffler and Silicon Graphics may not be used in any advertising or
  13. # publicity relating to the software without the specific, prior written
  14. # permission of Sam Leffler and Silicon Graphics.
  15. # THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  16. # EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  17. # WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  18. # IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  19. # ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  20. # OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  21. # WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  22. # LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  23. # OF THIS SOFTWARE.
  24. #
  25.  
  26. #
  27. # Print Statistics about Transmitted Facsimile.
  28. #
  29. SPOOL=/usr/spool/fax
  30. AWK=nawk
  31.  
  32. PATH=/bin:/usr/bin:/etc
  33. test -d /usr/ucb  && PATH=$PATH:/usr/ucb        # Sun and others
  34. test -d /usr/bsd  && PATH=$PATH:/usr/bsd        # Silicon Graphics
  35. test -d /usr/5bin && PATH=/usr/5bin:$PATH:/usr/etc    # Sun and others
  36. test -d /usr/sbin && PATH=/usr/sbin:$PATH        # 4.4BSD-derived
  37.  
  38. # look for an an awk: nawk, gawk, awk
  39. ($AWK '{}' </dev/null >/dev/null) 2>/dev/null ||
  40.     { AWK=gawk; ($AWK '{}' </dev/null >/dev/null) 2>/dev/null || AWK=awk; }
  41.  
  42. FILES=
  43. SORTKEY=-sender
  44. MAPNAMES=yes
  45.  
  46. while [ x"$1" != x"" ] ; do
  47.     case $1 in
  48.     -send*|-csi|-dest*|-speed|-rate|-format)
  49.         SORTKEY=$1;;
  50.     -nomap) MAPNAMES=no;;
  51.     -*)        echo "Usage: $0 [-sortkey] [-nomap] [files]"; exit 1;;
  52.     *)        FILES="$FILES $1";;
  53.     esac
  54.     shift
  55. done
  56. if [ -z "$FILES" ]; then
  57.     FILES=$SPOOL/etc/xferlog
  58. fi
  59.  
  60. #
  61. # Construct awk rules to collect information according
  62. # to the desired sort key.  There are two rules for
  63. # each; to deal with the two different formats that
  64. # have existed over time.
  65. #
  66. case $SORTKEY in
  67. -send*)
  68.     AWKRULES='$2 == "SEND" && NF == 9  { acct($3, $4, $7, $8, $5, $6, $9); }
  69.                $2 == "SEND" && NF == 11 { acct($4, $5, $9, $10, $7, $8, $11); }'
  70.     ;;
  71. -csi)
  72.     AWKRULES='$2 == "SEND" && NF == 9  { acct($4, $4, $7, $8, $5, $6, $9); }
  73.               $2 == "SEND" && NF == 11 { acct($6, $5, $9, $10, $7, $8, $11); }'
  74.     MAPNAMES=no
  75.     ;;
  76. -dest*)
  77.     AWKRULES='$2 == "SEND" && NF == 9  { acct($4, $4, $7, $8, $5, $6, $9); }
  78.               $2 == "SEND" && NF == 11 { acct($5, $5, $9, $10, $7, $8, $11); }'
  79.     MAPNAMES=no
  80.     ;;
  81. -speed|-rate)
  82.     AWKRULES='$2 == "SEND" && NF == 9  { acct($5, $4, $7, $8, $5, $6, $9); }
  83.               $2 == "SEND" && NF == 11 { acct($7, $5, $9, $10, $7, $8, $11); }'
  84.     MAPNAMES=no
  85.     ;;
  86. -format)
  87.     AWKRULES='$2 == "SEND" && NF == 9  { acct($6, $4, $7, $8, $5, $6, $9); }
  88.               $2 == "SEND" && NF == 11 { acct($8, $5, $9, $10, $7, $8, $11); }'
  89.     MAPNAMES=no
  90.     ;;
  91. esac
  92.  
  93. #
  94. # Generate an awk program to process the statistics file.
  95. #
  96. tmpAwk=/tmp/xfer$$
  97. trap "rm -f $tmpAwk; exit 1" 0 1 2 15
  98.  
  99. (cat<<'EOF'
  100. #
  101. # Convert hh:mm:ss to seconds.
  102. #
  103. func cvtTime(s)
  104. {
  105.     t = i = 0;
  106.     for (n = split(s, a, ":"); i++ < n; )
  107.     t = t*60 + a[i];
  108.     return t;
  109. }
  110.  
  111. func setupDigits()
  112. {
  113.   digits[0] = "0"; digits[1] = "1"; digits[2] = "2";
  114.   digits[3] = "3"; digits[4] = "4"; digits[5] = "5";
  115.   digits[6] = "6"; digits[7] = "7"; digits[8] = "8";
  116.   digits[9] = "9";
  117. }
  118.  
  119. #
  120. # Format seconds as hh:mm:ss.
  121. #
  122. func fmtTime(t)
  123. {
  124.     v = int(t/3600);
  125.     result = "";
  126.     if (v > 0) {
  127.     if (v >= 10)
  128.         result = digits[int(v/10)];
  129.     result = result digits[int(v%10)] ":";
  130.     t -= v*3600;
  131.     }
  132.     v = int(t/60);
  133.     if (v >= 10 || result != "")
  134.     result = result digits[int(v/10)];
  135.     result = result digits[int(v%10)];
  136.     t -= v*60;
  137.     return (result ":" digits[int(t/10)] digits[int(t%10)]);
  138. }
  139.  
  140. #
  141. # Setup a map for histogram calculations.
  142. #
  143. func setupMap(s, map)
  144. {
  145.     n = split(s, a, ":");
  146.     for (i = 1; i <= n; i++)
  147.     map[a[i]] = i;
  148. }
  149.  
  150. #
  151. # Add pages to a histogram.
  152. #
  153. func addToMap(key, ix, pages, map)
  154. {
  155.     if (key == "") {
  156.     for (i in map)
  157.         key = key ":";
  158.     }
  159.     n = split(key, a, ":");
  160.     a[map[ix]] += pages;
  161.     t = a[1];
  162.     for (i = 2; i <= n; i++)
  163.       t = t ":" a[i];
  164.     return t;
  165. }
  166.  
  167. #
  168. # Merge two histogram maps.
  169. #
  170. func mergeMap(map2, map1)
  171. {
  172.     if (map2 == "")
  173.     return map1;
  174.     else if (map1 == "")
  175.     return map2;
  176.     # map1 & map2 are populated
  177.     n1 = split(map1, a1, ":");
  178.     n2 = split(map2, a2, ":");
  179.     for (i = 1; i <= n1; i++)
  180.     a2[i] += a1[i];
  181.     t = a2[1];
  182.     for (i = 2; i <= n; i++)
  183.       t = t ":" a2[i];
  184.     return t;
  185. }
  186.  
  187. #
  188. # Return the name of the item with the
  189. # largest number of accumulated pages.
  190. #
  191. func bestInMap(totals, map)
  192. {
  193.    n = split(totals, a, ":");
  194.    imax = 1; max = -1;
  195.    for (j = 1; j <= n; j++)
  196.        if (a[j] > max) {
  197.        max = a[j];
  198.        imax = j;
  199.        }
  200.    split(map, a, ":");
  201.    return a[imax];
  202. }
  203.  
  204. #
  205. # Sort array a[l..r]
  206. #
  207. function qsort(a, l, r) {
  208.     i = l;
  209.     k = r+1;
  210.     item = a[l];
  211.     for (;;) {
  212.     while (i < r) {
  213.             i++;
  214.         if (a[i] >= item)
  215.         break;
  216.         }
  217.     while (k > l) {
  218.             k--;
  219.         if (a[k] <= item)
  220.         break;
  221.         }
  222.         if (i >= k)
  223.         break;
  224.     t = a[i]; a[i] = a[k]; a[k] = t;
  225.     }
  226.     t = a[l]; a[l] = a[k]; a[k] = t;
  227.     if (k != 0 && l < k-1)
  228.     qsort(a, l, k-1);
  229.     if (k+1 < r)
  230.     qsort(a, k+1, r);
  231. }
  232.  
  233. func cleanup(s)
  234. {
  235.     gsub("\"", "", s);
  236.     gsub("^ +", "", s);
  237.     gsub(" +$", "", s);
  238.     return s;
  239. }
  240.  
  241. func setupToLower()
  242. {
  243.     upperRE = "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]";
  244.     upper["A"] = "a"; upper["B"] = "b"; upper["C"] = "c";
  245.     upper["D"] = "d"; upper["E"] = "e"; upper["F"] = "f";
  246.     upper["G"] = "g"; upper["H"] = "h"; upper["I"] = "i";
  247.     upper["J"] = "j"; upper["K"] = "k"; upper["L"] = "l";
  248.     upper["M"] = "m"; upper["N"] = "n"; upper["O"] = "o";
  249.     upper["P"] = "p"; upper["Q"] = "q"; upper["R"] = "r";
  250.     upper["S"] = "s"; upper["T"] = "t"; upper["U"] = "u";
  251.     upper["V"] = "v"; upper["W"] = "w"; upper["X"] = "x";
  252.     upper["Y"] = "y"; upper["Z"] = "z";
  253. }
  254.  
  255. func toLower(s)
  256. {
  257.     if (match(s, upperRE) != 0) {
  258.     do {
  259.         c = substr(s, RSTART, 1);
  260.         gsub(c, upper[c], s);
  261.     } while (match(s, upperRE));
  262.     }
  263.     return s;
  264. }
  265.  
  266. #
  267. # Accumulate a statistics record.
  268. #
  269. func acct(key, dest, pages, time, br, df, status)
  270. {
  271.     status = cleanup(status);
  272.     if (length(status) > 11) {
  273.     msg = toLower(substr(status, 1, 11));
  274.     if (callFailed[msg])
  275.         return;
  276.     }
  277.     key = cleanup(key);
  278. EOF
  279. test "$MAPNAMES" = "yes" && cat<<'EOF'
  280.     #
  281.     # Try to merge unqualified names w/ fully qualified names
  282.     # by stripping the host part of the domain address and
  283.     # mapping unqualified names to a stripped qualified name.
  284.     #
  285.     n = split(key, parts, "@");
  286.     if (n == 2) {            # user@addr
  287.     user = parts[1];
  288.     if (user != "root" && user != "guest") {
  289.         addr = parts[2];
  290.         #
  291.         # Strip hostname from multi-part domain name.
  292.         #
  293.         n = split(addr, domains, ".");
  294.         if (n > 1) {        # e.g. flake.asd.sgi.com
  295.         l = length(domains[1])+1;
  296.         addr = substr(addr, l+1, length(addr)-l);
  297.         key = user "@" addr;
  298.         }
  299.         if (addrs[user] == "") {    # record mapped name
  300.         addrs[user] = addr;
  301.         } else if (addrs[user] != addr) {
  302.         if (!warned[user "@" addr]) {
  303.             warned[user "@" addr] = 1;
  304.             printf "Warning, address clash, \"%s\" and \"%s\".\n", \
  305.                user "@" addrs[user], user "@" addr
  306.         }
  307.         }
  308.     }
  309.     } else if (n != 1) {
  310.     printf "Warning, weird user address/name \"%s\".\n", key
  311.     }
  312. EOF
  313. cat<<'EOF'
  314.     dest = cleanup(dest);
  315.     sendpages[key] += pages;
  316.     time = cleanup(time);
  317.     if (pages == 0 && time > 60)
  318.     time = 0;
  319.     sendtime[key] += cvtTime(time);
  320.     if (status != "")
  321.     senderrs[key]++;
  322.     br = cleanup(br);
  323.     sendrate[key] = addToMap(sendrate[key], br, pages, rateMap);
  324.     df = cleanup(df);
  325.     senddata[key] = addToMap(senddata[key], df, pages, dataMap);
  326. }
  327.  
  328. #
  329. # Print a rule between the stats and the totals line.
  330. #
  331. func printRule(n, s)
  332. {
  333.     r = "";
  334.     while (n-- >= 0)
  335.     r = r s;
  336.     printf "%s\n", r;
  337. }
  338.  
  339. BEGIN        { FS="\t";
  340.           rates = "2400:4800:7200:9600:12000:14400";
  341.           setupMap(rates, rateMap);
  342.           datas = "1-D MR:2-D MR:2-D Uncompressed Mode:2-D MMR";
  343.           setupMap(datas, dataMap);
  344.           callFailed["busy signal"] = 1;
  345.           callFailed["unknown pro"] = 1;
  346.           callFailed["no carrier "] = 1;
  347.           callFailed["no local di"] = 1;
  348.           callFailed["no answer f"] = 1;
  349.           setupToLower();
  350.         }
  351. END        { OFS="\t"; setupDigits();
  352.           maxlen = 15;
  353.           # merge unqualified and qualified names
  354.           for (key in sendpages) {
  355.               if (addrs[key] != "") {
  356.               fullkey = key "@" addrs[key];
  357.               sendpages[fullkey] += sendpages[key];
  358.               sendtime[fullkey] += sendtime[key];
  359.               senderrs[fullkey] += senderrs[key];
  360.               sendrate[fullkey] = \
  361.                   mergeMap(sendrate[fullkey], sendrate[key]);
  362.               senddata[fullkey] = \
  363.                   mergeMap(senddata[fullkey], senddata[key]);
  364.               }
  365.           }
  366.           nsorted = 0;
  367.           for (key in sendpages) {
  368.               if (addrs[key] != "")    # unqualified name
  369.               continue;
  370.               l = length(key);
  371.               if (l > maxlen)
  372.             maxlen = l;
  373.               sorted[nsorted++] = key;
  374.           }
  375.           qsort(sorted, 0, nsorted-1);
  376.           fmt = "%-" maxlen "." maxlen "s";    # e.g. %-24.24s
  377.           printf fmt " %5s %8s %6s %4s %7s %7s\n",
  378.               "Destination", "Pages", "Time", "Pg/min",
  379.               "Errs", "TypRate", "TypData";
  380.           tpages = 0;
  381.           ttime = 0;
  382.           terrs = 0;
  383.           for (k = 0; k < nsorted; k++) {
  384.               i = sorted[k];
  385.               t = sendtime[i]/60; if (t == 0) t = 1;
  386.               n = sendpages[i]; if (n == 0) n = 1;
  387.               brate = best
  388.               printf fmt " %5d %8s %6.1f %4d %7d %7.7s\n",
  389.               i, sendpages[i], fmtTime(sendtime[i]),
  390.               sendpages[i] / t, senderrs[i],
  391.               bestInMap(sendrate[i], rates),
  392.               bestInMap(senddata[i], datas);
  393.             tpages += sendpages[i];
  394.             ttime += sendtime[i];
  395.             terrs += senderrs[i];
  396.           }
  397.           printRule(maxlen+1+5+1+8+6+1+4+1+7+1+7, "-");
  398.           t = ttime/60; if (t == 0) t = 1;
  399.           printf fmt " %5d %8s %6.1f %4d\n",
  400.               "Total", tpages, fmtTime(ttime), tpages/t, terrs;
  401.         }
  402. EOF
  403. echo "$AWKRULES"
  404. )>$tmpAwk
  405. $AWK -f $tmpAwk $FILES
  406.